package io.renren.common.utils;

import io.renren.modules.sys.dto.SysMenuDTO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;


@Component
@Slf4j
public class PermissionsUtils1 implements ApplicationContextAware {

    @Autowired
    private RequestMappingHandlerMapping requestMappingHandlerMapping;

    private static ApplicationContext applicationContext;


    public Set<String[]> test(Class<? extends Annotation> annotation) {
        List<Class<?>> inClassList = new ArrayList<>();
        Map<Class<?>, Set<Method>> containsMap = new HashMap<>();
        //获取到所有包含@RestController 或者 @Controller的bean
        Map<String, Object> beanList = applicationContext.getBeansWithAnnotation(RestController.class);
        beanList.forEach((k, v) -> inClassList.add(applicationContext.getType(k)));
        getMethodsWithAnnotationFromAllClasses(inClassList, annotation, containsMap);
        Set<String[]> resultList = new HashSet<>();
        //获取所有uri的集合
        Map<RequestMappingInfo, HandlerMethod> map = requestMappingHandlerMapping.getHandlerMethods();
        for (Map.Entry<Class<?>, Set<Method>> classEntry : containsMap.entrySet()) {
            Class<?> myClasss = classEntry.getKey();
            Api api = myClasss.getAnnotation(Api.class);


            Long id = new SequenceIdUtil().nextId();
            SysMenuDTO dto = new SysMenuDTO();
            dto.setId(id);
            dto.setType(0);
            dto.setName(api != null ? api.tags()[0] : null);
            dto.setPid(0L);//一级目录
            System.out.println("--Api:" + dto.getType() + "," + (api != null ? api.tags()[0] : null));

            for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : map.entrySet()) {
                HandlerMethod method = entry.getValue();

                Set<Method> str = classEntry.getValue();
                Set<String> methodNameSet = str.stream().map(Method::getName).collect(Collectors.toSet());
                //如果controller匹配到了containsMap的key且方法存在于containsMap的value里,将其uri加入resultList
                String controllerName = classEntry.getKey().getName();
                String methodName = method.getBeanType().getName();
                //controllerName拿到的是代理beanName，需要处理
                if (controllerName.contains("$$")) {
                    controllerName = controllerName.substring(0, controllerName.indexOf("$$"));
                }

                try {
                    if (controllerName.equals(methodName) && methodNameSet.contains(method.getMethod().getName())) {
                        ApiOperation apiOperation = method.getMethodAnnotation(ApiOperation.class);
                        RequiresPermissions permissions = method.getMethodAnnotation(RequiresPermissions.class);

                        SysMenuDTO dtoChildren = new SysMenuDTO();
                        Long id1 = new SequenceIdUtil().nextId();
                        dtoChildren.setId(id1);
                        dtoChildren.setName(apiOperation != null ? apiOperation.value() : null);
                        dtoChildren.setPid(dto.getId());//一级目录
                        dto.setType(1);
                        dtoChildren.setPermissions(permissions != null ? permissions.value()[0] : null);
                        System.out.println("----permissions:" + dto.getType() + "," + (apiOperation != null ? apiOperation.value() : null) + "->" + (permissions != null ? permissions.value()[0] : null));
                        //获取uri
                        for (RequestMethod m : entry.getKey().getMethodsCondition().getMethods()) {
                            for (String pattern : entry.getKey().getPatternsCondition().getPatterns()) {
                                String[] resultArr = new String[2];
                                resultArr[0] = m.name();
                                resultArr[1] = pattern;
                                resultList.add(resultArr);
                            }
                        }
                    }
                } catch (Exception e) {
                    log.error(e.toString());
                }
            }
        }
        return resultList;
    }

    /**
     * 获取类里所有包含annotation的方法名
     *
     * @param inClassList 需过滤其中方法的class集合
     * @param annotation  注解class
     * @param map         包含该annotation的map,key为controller类，集合中为方法
     */
    private void getMethodsWithAnnotationFromAllClasses(List<Class<?>> inClassList,
                                                        Class<? extends Annotation> annotation, Map<Class<?>, Set<Method>> map) {
        for (Class<?> myClasss : inClassList) {
            Method[] methods = myClasss.getMethods();
            Set<Method> methodList = new HashSet<>();
            for (Method method : methods) {
                //findAnnotation也可以拿到其父类是否包含该注解
                if (null != method && null != AnnotationUtils.findAnnotation(method, annotation)) {
                    try {
                        methodList.add(method);
                    } catch (Exception e) {
                        log.error(e.toString());
                    }
                }
            }
            if (methodList.size() > 0) {
                map.put(myClasss, methodList);
            }

        }

    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
